package com.tanhua.dubbo.api.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import com.mongodb.client.result.DeleteResult;
import com.tanhua.dubbo.api.VideoApi;
import com.tanhua.dubbo.enums.CommentType;
import com.tanhua.dubbo.enums.IdType;
import com.tanhua.dubbo.pojo.*;
import com.tanhua.dubbo.service.IdService;
import com.tanhua.dubbo.vo.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.jta.UserTransactionAdapter;

import java.util.ArrayList;
import java.util.List;

@DubboService(version = "1.0.0")
@Slf4j
public class VideoApiImpl implements VideoApi {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private IdService idService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    //视频评论数据存储在Redis中key的前缀
    private static final String COMMENT_REDIS_KEY_PREFIX = "VIDEO_COMMENT_";

    //用户是否点赞的前缀
    private static final String COMMENT_USER_LIKE_REDIS_KEY_PREFIX = "USER_LIKE_";

    //用户是否关注的前缀
    private static final String VIDEO_FOLLOW_USER_KEY_PREFIX = "VIDEO_FOLLOW_USER_";


    @Override
    public String saveVideo(Video video) {
        //校验
        if (!ObjectUtil.isAllNotEmpty(video.getUserId(), video.getVideoUrl(),
                video.getPicUrl())) {
            return null;
        }

        video.setId(ObjectId.get());
        video.setVid(this.idService.createId(IdType.VIDEO)); //自增长的id
        video.setCreated(System.currentTimeMillis());
        video.setSeeType(1);

        this.mongoTemplate.save(video);
        return video.getId().toString();
    }

    /**
     * 分页查询小视频列表，按照时间倒序排序，优先查询推荐结果
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Video> queryVideoList(Long userId, Integer page, Integer pageSize) {
        PageInfo<Video> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);

        //查询推荐的小视频数据
        String redisKey = "QUANZI_VIDEO_RECOMMEND_" + userId;
        String redisData = this.redisTemplate.opsForValue().get(redisKey);
        List<String> vidStrList = StrUtil.split(redisData, ',');
        List<Long> vidList = new ArrayList<>();
        if (CollUtil.isNotEmpty(vidStrList)) {
            //分页查询
            int[] startEnd = PageUtil.transToStartEnd(page - 1, pageSize);
            int startIndex = startEnd[0];
            int endIndex = Math.min(startEnd[1], vidStrList.size());

            for (int i = startIndex; i < endIndex; i++) {
                vidList.add(Convert.toLong(vidStrList.get(i)));
            }
        }

        if (CollUtil.isEmpty(vidList)) {
            //没有推荐 或 推荐数据已经查询完了，需要查询MongoDB最新的数据
            int totalPage = PageUtil.totalPage(vidStrList.size(), pageSize);
            PageRequest pageRequest = PageRequest.of(page - totalPage - 1, pageSize,
                    Sort.by(Sort.Order.desc("created")));
            Query query = new Query().with(pageRequest);
            List<Video> videoList = this.mongoTemplate.find(query, Video.class);
            pageInfo.setRecords(videoList);
        } else {
            //推荐逻辑
            pageInfo.setRecords(new ArrayList<>());
            Query query = Query.query(Criteria.where("vid").in(vidList));
            List<Video> videoList = this.mongoTemplate.find(query, Video.class);

            for (Long vid : vidList) {
                for (Video video : videoList) {
                    if (ObjectUtil.equal(vid, video.getVid())) {
                        pageInfo.getRecords().add(video);
                        break;
                    }
                }
            }
        }

        return pageInfo;
    }

    /**
     * 根据id查询视频
     *
     * @param videoId
     * @return
     */
    @Override
    public Video queryVideoById(String videoId) {
        return this.mongoTemplate.findById(new ObjectId(videoId), Video.class);
    }

    /**
     * @Author 张园
     * 根据id查询评论
     *
     * @param commentId
     * @return
     */
    @Override
    public VideoComment queryCommentById(String commentId) {
        return this.mongoTemplate.findById(new ObjectId(commentId), VideoComment.class);
    }

    /**
     * @Author 张园
     * 保存Comment
     *
     * @param userId
     * @param publishId  传入videoId,就是给视频的评论；传入commentId,就是给评论的评论
     * @param commentType
     * @param content
     * @return
     */
    private Boolean saveComment(Long userId, String publishId,
                                CommentType commentType, String content) {
        try {
            VideoComment videoComment = new VideoComment();
            videoComment.setId(ObjectId.get());
            videoComment.setPublishId(new ObjectId(publishId));
            // 评论类型
            videoComment.setCommentType(commentType.getType());
            // 内容
            videoComment.setContent(content);
            videoComment.setUserId(userId);
            videoComment.setCreated(System.currentTimeMillis());
            //查询小视频
            Video video = this.queryVideoById(publishId);
            if(ObjectUtil.isNotEmpty(video)){
                //发布视频的用户id
                videoComment.setPublishUserId(video.getUserId());
            }else{
                //查询评论
                VideoComment myVideoComment=this.queryCommentById(publishId);
                if(ObjectUtil.isNotEmpty(myVideoComment)){
                    //发布评论的用户id
                    videoComment.setPublishUserId(myVideoComment.getUserId());
                }else{
                    return false;
                }
            }
            this.mongoTemplate.save(videoComment);
            return true;
        } catch (Exception e) {
            log.error("保存Comment出错~ userId = " + userId + ", publishId = "
                    + publishId + ", commentType = " + commentType, e);
        }
        return false;
    }

    /**
     * @Author 张园
     * 点赞
     *
     * @param userId
     * @param publishId
     * @param commentType
     * @Param content
     * @return
     */
    @Override
    public Boolean likeComment(Long userId, String publishId,CommentType commentType,String content) {
        //判断用户是否已经点赞
        boolean isLike = this.queryUserIsLike(userId, publishId);
        if(isLike){
            //已经点赞，不需要在点赞
            return false;
        }
        try {
            //存储到MongoDB
            this.saveComment(userId,publishId,commentType,content);

            //在redis中添加用户点赞
            String redisKey = this.getCommentRedisKey(publishId);
            String likeHashKey = this.getCommentUserLikeRedisKey(userId);
            this.redisTemplate.opsForHash().put(redisKey, likeHashKey, "1");

            //在redis中更新点赞数
            String likeCountHashKey = commentType.name();
            if (this.redisTemplate.opsForHash().hasKey(redisKey, likeCountHashKey)) {
                //redis存在，点赞数自增1
                this.redisTemplate.opsForHash().increment(redisKey, likeCountHashKey, 1);
            } else {
                //redis无效,从mongdb查询点赞数,并存放到redis中
                Long likeCommentCount = this.queryCommentCountFromDB(publishId, commentType.getType());
                this.redisTemplate.opsForHash().put(redisKey, likeCountHashKey, Convert.toStr(likeCommentCount));
            }
            return true;
        } catch (Exception e) {
            log.error("点赞失败~ userId = " + userId + ", publishId = " + publishId, e);
        }
        return false;
    }

    private String getCommentRedisKey(String publishId) {
        return COMMENT_REDIS_KEY_PREFIX + publishId;
    }

    private String getCommentUserLikeRedisKey(Long userId) {
        return COMMENT_USER_LIKE_REDIS_KEY_PREFIX+userId;
    }

    /**
     * @Author 张园
     * 点赞取消
     *
     * @param publishId
     * @return
     */
    @Override
    public Boolean dislikeComment(Long userId, String publishId,CommentType commentType) {
        //判断用户是否已经点赞
        boolean isLike = this.queryUserIsLike(userId, publishId);
        if(!isLike){
            //用户没有点赞，不需要再取消点赞
            return false;
        }
        try {
            //从MongoDB删除该点赞评论
            Query query = Query.query(Criteria.where("userId").is(userId)
                    .and("publishId").is(new ObjectId(publishId))
                    .and("commentType").is(commentType.getType()));
            DeleteResult deleteResult = this.mongoTemplate.remove(query, VideoComment.class);

            //在redis中删除用户点赞
            String redisKey = this.getCommentRedisKey(publishId);
            String likeHashKey = this.getCommentUserLikeRedisKey(userId);
            this.redisTemplate.opsForHash().delete(redisKey, likeHashKey);

            //在redis中更新点赞数
            String likeCountHashKey = commentType.name();
            if (this.redisTemplate.opsForHash().hasKey(redisKey, likeCountHashKey)) {
                //redis存在，点赞数自增-1
                this.redisTemplate.opsForHash().increment(redisKey, likeCountHashKey, -1);
            } else {
                //redis无效,从mongdb查询点赞数,并存放到redis中
                Long likeCommentCount = this.queryCommentCountFromDB(publishId, commentType.getType());
                this.redisTemplate.opsForHash().put(redisKey, likeCountHashKey, Convert.toStr(likeCommentCount));
            }
            return true;
        } catch (Exception e) {
            log.error("取消点赞失败~ userId = " + userId + ", publishId = " + publishId, e);
        }
        return false;
    }

    /**
     * @Author 张园
     * 查询点赞数,评论数
     *
     * @param publishId (传入videoId,就是给视频的点赞数；传入commentId,就是给评论的点赞数)
     * @return
     */
    @Override
    public Long queryUserCommentCount(String publishId,CommentType commentType) {
        String redisKey=this.getCommentRedisKey(publishId);
        String LikeCountHashKey=commentType.name();
        Object data = this.redisTemplate.opsForHash().get(redisKey, LikeCountHashKey);
        if(ObjectUtil.isNotEmpty(data)){
            //redis有效
            return Convert.toLong(data);
        }
        //redis数据丢失，从MongoDB查询点赞数
        Long count = this.queryCommentCountFromDB(publishId, commentType.getType());
        return count;
    }

    /**
     * @Author 张园
     * 用户是否点赞
     *
     * @param userId
     * @param publishId (传入videoId,就是视频是否点赞；传入commentId,就是评论是否点赞)
     * @return
     */
    @Override
    public Boolean queryUserIsLike(Long userId, String publishId) {
        String redisKey=this.getCommentRedisKey(publishId);
        String likeHashKey=this.getCommentUserLikeRedisKey(userId);
        Boolean result = this.redisTemplate.opsForHash().hasKey(redisKey, likeHashKey);//redis是否有这个hashkey
        if(result){
            //redis存在，用户已经喜欢该动态
            return true;
        }
        //为防止redis不存在导致result为false，从mongdb查询用户是否点赞
        if(this.commentIsExist(userId, publishId)){
            return true;
        }
        return false;
    }

    /**
     * @Author 张园
     * 从mondb查询用户是否点赞
     *
     * @param publishId (传入videoId,就是视频是否点赞；传入commentId,就是评论是否点赞)
     * @return
     */
    private boolean commentIsExist(Long userId,String publishId){
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(CommentType.LIKE.getType())
                .and("userId").is(userId));
        return this.mongoTemplate.exists(query, VideoComment.class);
    }

    /**
     * @Author 张园
     * 从mongdb查询点赞数，评论数
     *
     * @param publishId  (传入videoId,就是视频点赞数；传入commentId,就是评论的点赞数)
     * @param commentType
     * @return
     */
    private Long queryCommentCountFromDB(String publishId,Integer commentType){
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType));
        return this.mongoTemplate.count(query, VideoComment.class);
    }

    /**
     * @Author 张园
     * 查询评论列表
     *
     * @param publishId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<VideoComment> queryCommentsList(String publishId, Integer page, Integer pageSize) {
        PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.asc("created")));
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(CommentType.COMMENT.getType()))
                .with(pageRequest);
        //查询评论列表
        List<VideoComment> videoComments = this.mongoTemplate.find(query, VideoComment.class);
        PageInfo<VideoComment> pageInfo = new PageInfo<>();
        pageInfo.setRecords(videoComments);
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        return pageInfo;
    }

    /**
     * @Author 张园
     * 提交评论
     * @param videoId
     * @param content
     * @param userId
     * @return
     */
    @Override
    public Boolean publishComments(String videoId, String content,Long userId) {

        return this.saveComment(userId, videoId, CommentType.COMMENT, content);
    }

    /**
     * @Author 张园
     * 关注用户
     *
     * @param userId 当前用户
     * @param followUserId 关注的目标用户
     * @return
     */
    @Override
    public Boolean followUser(Long userId, Long followUserId) {
        if(!ObjectUtil.isAllNotEmpty(userId,followUserId)) {
            return null;
        }
        try {
            //判断用户是否已经关注
            Boolean result = this.isFollowUser(userId, followUserId);
            if(result){
                //已关注，不能在关注
                return false;
            }
            //保存到mongdb
            FollowUser followUser = new FollowUser();
            followUser.setId(ObjectId.get());
            followUser.setUserId(userId);
            followUser.setFollowUserId(followUserId);
            followUser.setCreated(System.currentTimeMillis());
            this.mongoTemplate.save(followUser);

            //数据保存到redis，方便快速查询
            String redisKey = VIDEO_FOLLOW_USER_KEY_PREFIX+userId;
            String hashKey =StrUtil.toString(followUserId);
            this.redisTemplate.opsForHash().put(redisKey,hashKey,"1");
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * @Author 张园
     * 取消关注用户
     *
     * @param userId 当前用户
     * @param followUserId 关注的目标用户
     * @return
     */
    @Override
    public Boolean disFollowUser(Long userId, Long followUserId) {
        if(!ObjectUtil.isAllNotEmpty(userId,followUserId)) {
            return null;
        }
        try {
            //判断用户是否已经关注
            if(!(this.isFollowUser(userId, followUserId))){
                //没有关注，不能在取消关注
                return false;
            }
            //删除mongdb的数据
            Query query = Query.query(Criteria.where("userId").is(userId)
                    .and("followUserId").is(followUserId));
            DeleteResult deleteResult = this.mongoTemplate.remove(query, FollowUser.class);
            if(deleteResult.getDeletedCount()==0){
                return false;
            }
            //从redis中删除数据
            String redisKey = VIDEO_FOLLOW_USER_KEY_PREFIX+userId;
            String hashKey =StrUtil.toString(followUserId);
            this.redisTemplate.opsForHash().delete(redisKey,hashKey);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * @Author 张园
     * 查询用户是否关注某个用户
     *
     * @param userId 当前用户
     * @param followUserId 关注的目标用户
     * @return
     */
    @Override
    public Boolean isFollowUser(Long userId, Long followUserId) {
        String redisKey = VIDEO_FOLLOW_USER_KEY_PREFIX+userId;
        String hashKey =StrUtil.toString(followUserId);
        //从redis查询户是否关注某个用户
        Boolean result = this.redisTemplate.opsForHash().hasKey(redisKey, hashKey);
        if(result){
            //已关注
            return true;
        }
        //为防止redis失效导致result为false，从mongdb查询用户是否关注
        Query query = Query.query(Criteria.where("userId").is(userId)
                .and("followUserId").is(followUserId));
       if(this.mongoTemplate.exists(query, VideoComment.class)){
           //已关注
           return true;
       };
        return false;
    }
}
